#include "structure_pointers.h"

#include <prompt.h>
#include <headings.h>

#include <string.h> // for memset
#include <iostream>

using namespace std;

struct some_structure
{
    int integer_member;
    float float_member;
    const char *pointer_member;
};

static /* the static keyword has many meanings. here it means that the
        * function is only visible in the scope of the file it is
        * defined in. this way we need not be paranoid about name
        * collisions.  */
void some_function_that_takes_a_structure_and_modifies_it(some_structure *s)
{
    s->integer_member = 42;
    s->float_member = 3.142;
    s->pointer_member = "Hallo!";
}

void show_structure_pointers()
{
    HEADING2("Structure pointers");
    cout << "  Shows how to define and initialize structures.\n";
    cout << "  Passes a structure to a function that modifies it.\n\n";

    struct some_structure s;
    
    // in C there is no constructor. structures are generally
    // initialized by the most general constructor, memset(), by
    // setting all their memory to zero.

    memset(&s, 0, sizeof(s));

    // see what the members are

    cout << "  Members after memsetting to 0:\n";
    cout << "   integer_member = " << s.integer_member << '\n';
    cout << "   float_member = " << s.float_member << '\n';
    // at address 0 there's nothing meaningful for sure, so we just
    // print out the raw pointer value.
    cout << "   pointer_member = (void*)" << (void*)s.pointer_member << '\n';

    // call a function that takes our structure as "in-out"
    // parameter. (in C, there's no concept of in, out, in-out - it's
    // all pointers).

    cout << "  Passing the structure to modifying function ...\n";

    // the memory for our some_structure instance has been allocated
    // ON THE STACK (a so-called "automatic" allocation). unlike
    // allocation using malloc(), this is no pointer. so we have to
    // take the address (a pointer is nothing but an address), using
    // the address operator "&". we already saw this in the call to
    // memset() above.

    some_function_that_takes_a_structure_and_modifies_it(&s);

    // we have passed a pointer to the memory that we own, so we see
    // the modifications. it's probably best to think of pointer just
    // like you think of Java references, only without garbage
    // collection.
    
    cout << "  Members after modification:\n";
    cout << "   integer_member = " << s.integer_member << '\n';
    cout << "   float_member = " << s.float_member << '\n';
    // some_function_that_takes_a_structure_and_modifies_it() is
    // documented to let the char* member point to a well defined
    // string. print out both the raw pointer values and the memory it
    // points to.
    cout << "   pointer_member = (void*)" << (void*)s.pointer_member << ", (char*)\"" << s.pointer_member << "\"\n";
}
